Skip to content

tests: virtual HID device test harness (Linux/Windows/macOS/libusb)#815

Draft
Youw wants to merge 2 commits into
masterfrom
virtual-device-tests
Draft

tests: virtual HID device test harness (Linux/Windows/macOS/libusb)#815
Youw wants to merge 2 commits into
masterfrom
virtual-device-tests

Conversation

@Youw
Copy link
Copy Markdown
Member

@Youw Youw commented Jun 7, 2026

Summary

Adds a small, reusable virtual HID device test harness so HIDAPI can be tested without physical hardware. Tests are written purely against the public HIDAPI API plus a backend-agnostic test_virtual_device interface, so the same test runs against every backend that ships a provider.

Devices expose pre-recorded scenarios: the test sends a Feature report whose first payload byte is a command, and the device replays the matching canned input report — keeping the test code 100% platform-neutral.

Intended as a foundation further tests can build on (e.g. the hid_read_interrupt test in #799 can rebase onto this harness).

What's here

  • src/tests/test_device_io.c — a simple open → write → exchange reports (Feature trigger / input read) → close smoke test.
  • Four virtual-device providers, each self-skipping (CTest code 77) when its device can't be created:
Platform / backend Provider Mechanism CI
Linux / hidraw test_virtual_device_uhid.c kernel /dev/uhid runs + passes every push (ubuntu-cmake)
Linux / libusb test_virtual_device_rawgadget.c /dev/raw-gadget + dummy_hcd, inside a VM self-skips per push; runs + passes in the manual libusb-vhid-test (VM)
Windows / winapi test_virtual_device_win.c + windows/driver/ modified vhidmini2 UMDF2 driver self-skips per push; runs + passes in the manual win-vhid-test
macOS / darwin test_virtual_device_mac.c IOHIDUserDevice (IOKit) self-skips per push (macos-cmake); runs on a real Mac

See src/tests/README.md for details, including why FreeBSD/NetBSD/OpenBSD have no provider (no userspace HID/USB-create facility on any BSD).

CI

  • Per-push (builds.yml): the test is built on all platforms and run where possible. ubuntu-cmake loads uhid and runs DeviceIO_hidraw for real, while DeviceIO_libusb builds but self-skips there (the hosted kernel has no USB gadget subsystem); Windows and macOS likewise build the test and self-skip — so the matrix stays green everywhere.
  • win-vhid-test.yml (manual): builds, self-signs and installs the vhidmini2 driver on a hosted windows-latest runner and runs DeviceIO_winapi against the real device. ✅ verified passing.
  • libusb-vhid-test.yml (manual): the hosted ubuntu-latest (azure) kernel has no USB gadget subsystem, so this runs DeviceIO_libusb inside a lightweight virtme-ng + QEMU VM that boots a generic kernel (building dummy_hcd out-of-tree and loading raw_gadget), against a real virtual USB device. ✅ verified passing. The same approach works locally and on WSL2.

The two privileged jobs run only via workflow_dispatch or when a pull request carries the ci-virtual-device label, so they stay out of the per-push matrix.

Commits

  1. tests: add a backend-agnostic virtual HID device test harness
  2. ci: build and run the virtual HID device tests

Drafted with Claude Code.

@Youw Youw added the ci-virtual-device Run the virtual HID device CI jobs (Windows driver + libusb raw-gadget) label Jun 7, 2026
@mcuee
Copy link
Copy Markdown
Member

mcuee commented Jun 7, 2026

Great. We need this kind of virtual test device for hidapi (and also libusb).

@Youw Youw force-pushed the virtual-device-tests branch from 1cdc8d5 to b330600 Compare June 7, 2026 11:54
@mcuee
Copy link
Copy Markdown
Member

mcuee commented Jun 7, 2026

@Youw

FYI on WDK, the Windows runners have WDK installed already.

Nuget WDK -- smaller but lacking some tools, which may be okay for this purpose
Built-in WDK -- good enough
EWDK -- great for local build of libusb-win32/libusbK but not good for CI.

Reference:

@Youw Youw force-pushed the virtual-device-tests branch 2 times, most recently from 5a24890 to 6089973 Compare June 7, 2026 13:51
@Youw
Copy link
Copy Markdown
Member Author

Youw commented Jun 7, 2026

Thanks @mcuee, that's useful. The win-vhid-test job currently downloads WDK 26100.6584 explicitly because the first preinstalled option I tried (the Chocolatey windowsdriverkit11 VSIX) was VS2019-only, so msbuild under the VS2022 toolset couldn't find wdf.h. Good to know the built-in (or NuGet) WDK should be enough — I'll try pointing the job at the preinstalled/NuGet WDK to drop that ~2–3 min download and the external fetch, and confirm the WDF headers resolve against the VS2022 toolset. Will report back.

This reply was generated by Claude Code at my request.

@Youw
Copy link
Copy Markdown
Member Author

Youw commented Jun 7, 2026

This reply was generated by Claude Code at my request.

https://www.instagram.com/p/DXcOy5riYPS/

(I home community don't mind a little humor here)

@Youw Youw force-pushed the virtual-device-tests branch 2 times, most recently from d3877f1 to 35b5186 Compare June 7, 2026 19:41
@Youw
Copy link
Copy Markdown
Member Author

Youw commented Jun 7, 2026

Followed up on the WDK tip — thanks again. On the current windows-latest image the runner ships the Windows SDK (10.0.26100) but not the WDK: there are no UMDF (wdf) headers under Windows Kits\10\Include, so the vhidmini2 UMDF2 build still needs the WDK installed.

So rather than dropping the download, I cached it. The win-vhid-test job now caches a self-contained offline WDK installer layout (actions/cache@v4, keyed by WDK version) and installs from it — the WDK is downloaded only once and restored from cache on later runs. Verified: on a cache hit the download step is skipped, the driver builds, and DeviceIO_winapi passes. I also kept a preinstalled-WDK fast-path, so if a future image ships the WDK with UMDF headers it's used automatically with no download.

(The NuGet WDK would be a cleaner long-term route, but it needs reworking the legacy .vcxproj, so I left that for later.)

Posted by Claude Code at my request.

@Youw Youw force-pushed the virtual-device-tests branch from 35b5186 to a0792ee Compare June 7, 2026 21:09
Youw added 2 commits June 8, 2026 00:26
Add a reusable harness that runs HIDAPI tests against a *virtual* HID device,
needing no physical hardware. Tests use only the public HIDAPI API plus a
backend-agnostic test_virtual_device interface, so the same test runs against
every backend that ships a virtual-device provider. Devices expose pre-recorded
"scenarios": the test sends a Feature report carrying a command byte and the
device replays the matching canned input report.

- src/tests/test_device_io.c: a simple open -> write -> exchange reports ->
  close smoke test (skips via CTest code 77 when no virtual device is present).
- Providers:
    test_virtual_device_uhid.c       Linux hidraw  (kernel /dev/uhid)
    test_virtual_device_rawgadget.c  Linux libusb  (/dev/raw-gadget + dummy_hcd)
    test_virtual_device_win.c (+windows/driver/)   Windows winapi (vhidmini2 UMDF2)
    test_virtual_device_mac.c        macOS darwin  (IOHIDUserDevice)
  Each self-skips when its device can't be created.
- HIDAPI_WITH_TESTS is offered on Linux/macOS too; src/tests builds the test for
  whichever backend(s) are present.
- src/tests/README.md documents the providers, the scenario protocol, and why
  FreeBSD/NetBSD/OpenBSD have no provider (no userspace HID/USB-create facility).
- src/tests/windows/driver/ vendors a modified Microsoft vhidmini2 UMDF2 sample
  under its original Microsoft Public License (MS-PL); LICENSE.txt and README.md
  there record the provenance and modifications and keep it separate from
  HIDAPI's own license (the driver is a standalone test fixture, not linked into
  the library).

Assisted-by: Claude:claude-opus-4.8
- builds.yml: build the test on Linux/Windows/macOS and run it where possible
  (ubuntu-cmake loads uhid and runs DeviceIO_hidraw; Windows/macOS/libusb build
  and self-skip), keeping the per-push matrix green.
- win-vhid-test.yml: build, self-sign and install the vhidmini2 driver on a
  hosted windows-latest runner and run DeviceIO_winapi against it.
- libusb-vhid-test.yml (+ .github/vmrun-libusb.sh): the hosted kernel has no USB
  gadget subsystem, so run DeviceIO_libusb inside a virtme-ng VM that boots a
  generic kernel (building dummy_hcd out-of-tree and loading raw_gadget) against
  a real virtual USB device.

Both privileged jobs run only via workflow_dispatch or a 'ci-virtual-device'
pull-request label, so they stay out of the per-push matrix.

Assisted-by: Claude:claude-opus-4.8
@Youw Youw force-pushed the virtual-device-tests branch from a0792ee to 3ae4b91 Compare June 7, 2026 21:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-virtual-device Run the virtual HID device CI jobs (Windows driver + libusb raw-gadget)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants